#include "Precomp.h"
#include "Game.h"
#include "IGameState.h"
#include "MapFactory.h"
#include <sstream>
#include "MapState.h"

namespace SGF
{
	Game* Game::instance=0;

	Game::Game()
	:currentState(0),mapFactory(0),root(0),window(0)
	{
		assert(Game::instance==0);
		Game::instance=this;
	}

	Game::~Game()
	{
		currentState->onLeave();
		inputManager->destroyInputObject(keyboard);
		inputManager->destroyInputObject(mouse);
		OIS::InputManager::destroyInputSystem(inputManager);
		delete root;
		delete mapFactory;
	}

	Game* Game::getInstance()
	{
		return Game::instance;
	}

	IGameState* Game::getCurrentState() const
	{
		return currentState;
	}

	void Game::changeState(SGF::IGameState *newState)
	{
		if(currentState)
			currentState->onLeave();
		currentState=newState;
		currentState->onEnter();
	}

	void Game::changeMap(const char* newMap)
	{
		currentState->onLeave();
		currentState=mapFactory->createMapFromFile(newMap);
		currentState->onEnter();
		
	}

	bool Game::initialize()
	{
		if(!initializeOgre())
			return false;
		createMapFactory();
		initializeOIS();
		return true;
	}

	bool Game::initializeOgre()
	{
		root=new Ogre::Root;
		if(!root->restoreConfig())
			if(!root->showConfigDialog())
				return false;
		window=root->initialise(true,"Game window");
		window->addViewport(0);
		root->addFrameListener(this);
		return true;
	}

	void Game::initializeOIS()
	{
		OIS::ParamList pl;
		size_t windowHnd = 0;
		std::ostringstream windowHndStr;

		window->getCustomAttribute("WINDOW", &windowHnd);
		windowHndStr << windowHnd;
		pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));

		inputManager = OIS::InputManager::createInputSystem( pl );

		//!Create all devices.
		keyboard = static_cast<OIS::Keyboard*>(inputManager->createInputObject( OIS::OISKeyboard, true ));
		mouse = static_cast<OIS::Mouse*>(inputManager->createInputObject( OIS::OISMouse, true ));

		unsigned int width, height, depth;
		int left, top;
		window->getMetrics(width, height, depth, left, top);
		mouse->getMouseState().width = width;
		mouse->getMouseState().height = height;
	}

	void Game::createMapFactory()
	{
		mapFactory=new MapFactory<>(this);
	}

	void Game::run()
	{
		running=true;
		root->startRendering();
	}

	Ogre::Root* Game::getOgreRoot() const
	{
		return root;
	}

	Ogre::RenderWindow* Game::getRenderWindow() const
	{
		return window;
	}

	MapFactoryBase* Game::getMapFactory() const
	{
		return mapFactory;
	}

	void Game::quit()
	{
		running=false;
	}

	bool Game::frameStarted(const Ogre::FrameEvent& ev)
	{
		mouse->capture();
		keyboard->capture();
		currentState->update(ev.timeSinceLastFrame);
		return true;
	}

	bool Game::frameEnded(const Ogre::FrameEvent& ev)
	{
		return running&&(!(window->isClosed()));
	}

	bool Game::keyPressed( const OIS::KeyEvent &arg )
	{
		keyPressedSignal(KeyEvent(arg));
		return true;
	}

	bool Game::keyReleased( const OIS::KeyEvent &arg )
	{
		keyReleasedSignal(KeyEvent(arg));
		return true;
	}

	bool Game::mouseMoved( const OIS::MouseEvent &arg )
	{
		mouseMovedSignal(MouseEvent(arg,OIS::MB_Left));//mouse button is invalid here
		return true;
	}

	bool Game::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
	{
		mousePressedSignal(MouseEvent(arg,id));
		return true;
	}

	bool Game::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
	{
		mouseReleasedSignal(MouseEvent(arg,id));
		return true;
	}
}